﻿/****************************** Module Header ******************************\
 * Module Name:  ProgramMutex.cs
 * Module Version:  1.0.0 (2012-10-22)
 * Copyright (c) AnyEB.Com (14507247@qq.com)
 * 
 * This class is use for run a mutex program. 
 * 
 * This source is subject to the GPL License.
 * See http://www.gnu.org/licenses/gpl.html.
 * All other rights reserved.
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;

namespace UCL.Forms
{
    /// <summary>
    /// 程序单例运行(防止重复打开程序)
    /// <para>[STAThread]</para>
    /// <para>static void Main()</para>
    /// <para>{</para>
    /// <para>    System.Diagnostics.Process instance =ProgramMutexHelper.RunningInstance(System.Reflection.Assembly.GetExecutingAssembly());</para>
    /// <para>    if (instance == null)</para>
    /// <para>    { </para>
    /// <para>        Application.EnableVisualStyles();</para>
    /// <para>        Application.SetCompatibleTextRenderingDefault(false);</para>
    /// <para></para>
    /// <para>        new frmLogin().Show();</para>
    /// <para>        Application.Run();</para>
    /// <para>    }</para>
    /// <para>    else</para>
    /// <para>    {</para>
    /// <para>        ProgramMutexHelper.HandleRunningInstance(instance, ProgramMutexHelper.FindWindow(null, "GTD"));</para>
    /// <para>    }</para>
    /// <para>}</para>
    /// </summary>
    public class ProgramMutexHelper
    {
        // <summary> 
        /// 该函数设置由不同线程产生的窗口的显示状态。 
        /// </summary> 
        /// <param name="hWnd">窗口句柄</param> 
        /// <param name="cmdShow">指定窗口如何显示。查看允许值列表，请查阅ShowWlndow函数的说明部分。</param> 
        /// <returns>如果函数原来可见，返回值为非零；如果函数原来被隐藏，返回值为零。</returns>
        [DllImport("User32.dll")]
        public static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);

        /// <summary> 
        /// 该函数将创建指定窗口的线程设置到前台，并且激活该窗口。键盘输入转向该窗口，并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。 
        /// </summary> 
        /// <param name="hWnd">将被激活并被调入前台的窗口句柄。</param> 
        /// <returns>如果窗口设入了前台，返回值为非零；如果窗口未被设入前台，返回值为零。</returns> 
        [DllImport("User32.dll", EntryPoint = "SetForegroundWindow")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary>
        /// 通过类名或者窗体名称寻找窗体句柄
        /// <para>如：FindWindow("notepad",null);</para>
        /// <para>FindWindow(null,"GTD");</para>
        /// </summary>
        /// <param name="lpClassName"></param>
        /// <param name="lpWindowName"></param>
        /// <returns></returns>
        /// <remarks>2013-10-8 by sutroon</remarks>
        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);


        private const int WS_SHOWNORMAL = 1;

        /// <summary>
        /// 获取正在运行的实例，没有运行的实例返回null; 
        /// </summary>
        /// <param name="assCurrent">System.Reflection.Assembly.GetExecutingAssembly()</param>
        /// <returns></returns>
        public static Process RunningInstance(Assembly assCurrent)
        {
            Process current = Process.GetCurrentProcess();
            Process[] processes = Process.GetProcessesByName(current.ProcessName);
            foreach (Process process in processes)
            {
                if (process.Id != current.Id)
                {
                    if (assCurrent.Location.Replace("/", "//") == current.MainModule.FileName)
                    {
                        return process;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 显示已运行的程序
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="hwnd"></param>
        public static void HandleRunningInstance(Process instance, IntPtr hwnd)
        {
            if (instance.MainWindowHandle.ToInt32() != 0)
            {
                ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);//显示，可以注释掉 
                SetForegroundWindow(instance.MainWindowHandle);//放到前端 
            }
            else
            {
                ShowWindowAsync(hwnd, WS_SHOWNORMAL);
                SetForegroundWindow(hwnd);
            }
        }

        /// <summary>
        /// 检测是否有残余的进程
        /// <para>用于系统出错的时候，进程未正常关闭，启动新程序的时候无法正常打开界面</para>
        /// </summary>
        /// <param name="currentAssem"></param>
        /// <remarks>2014-3-28 1.0 by sutroon</remarks>
        public static void CheckExistThread(Assembly currentAssem)
        {
            Process current = Process.GetCurrentProcess();
            Process[] processes = Process.GetProcessesByName(current.ProcessName);
            List<Process> lstP = new List<Process>();
            foreach (Process process in processes)
            {
                if (process.Id != current.Id)
                {
                    if (currentAssem.Location.Replace("/", "//") == current.MainModule.FileName)
                    {
                        lstP.Add(process);
                    }
                }
            }
            if (lstP.Count == 0) return;
            if (DialogResult.OK != MessageBox.Show(string.Format("发现已有 [{0}] 个相同的程序在后台运行,是否强制关闭较早的后台进程?", lstP.Count), "提示", MessageBoxButtons.OKCancel)) return;
            foreach (Process p in lstP)
            {
                p.Kill();
                System.Threading.Thread.Sleep(500);
            }
        }
    }
}
